package uk.ljmu.qudexapps.swfspreadsheet.domain;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;

import uk.ljmu.metadata.staticschemas.FedoraRelsExtOntology;
import uk.ljmu.metadata.staticschemas.GeoSchema;
import uk.ljmu.metadata.staticschemas.StaticDDI;
import uk.ljmu.metadata.staticschemas.StaticJenaDublinCore;
import uk.ljmu.metadata.staticschemas.StaticJenaDublinCoreTerms;
import uk.ljmu.metadata.staticschemas.StaticJenaRDFS;
import uk.ljmu.metadata.staticschemas.StaticQudex;
import uk.ljmu.metadata.staticschemas.StaticSkos;
import uk.ljmu.qudexapps.swfspreadsheet.utilities.Constants;
import uk.ljmu.qudexapps.swfspreadsheet.utilities.RdfFunctions;

public class MetadataFlowSchemas {

	private static Logger log = Logger.getLogger(MetadataFlowSchemas.class);
	
	private boolean schemasPresent = false;
	
	/**
	 * 0 - Collections Spreadsheet 1 - Documents Spreadsheet
	 */
	private int typeSS;

	/**
	 * Holds the elements needed to generate either qudex collections or files
	 * spreadsheets
	 */
	private Elements qudexElements;

	/**
	 * @param typeSS
	 *            the typeSS to set
	 */
	public void setTypeSS(int typeSS) {
		this.typeSS = typeSS;
	}

	/**
	 * @return the typeSS
	 */
	public int getTypeSS() {
		return typeSS;
	}

	/**
	 * @param qudexElements
	 *            the qudexElements to set
	 */
	public void setQudexElements(Elements qudexElements) {
		this.qudexElements = qudexElements;
	}

	/**
	 * @return the qudexElements
	 */
	public Elements getQudexElements() {
		return qudexElements;
	}

	/**
	 * List with name-sensible name pairs for each schema available - Object
	 * MdSchema
	 */
	private List<MdSchema> availableSchemas;

	/**
	 * List holding the schemas selected by the user to include in the
	 * spreadsheet List of MdSchemaInstance objects: name-sensible name + List
	 * with selected elements from the particular schema
	 */
	private List<MdSchemaInstance> mdSchemaInstanceContainer;

	/**
	 * @return the mdSchemaInstanceContainer
	 */
	public List<MdSchemaInstance> getMdSchemaInstanceContainer() {
		return mdSchemaInstanceContainer;
	}

	/**
	 * @param mdSchemaInstanceContainer
	 *            the mdSchemaInstanceContainer to set
	 */
	public void setMdSchemaInstanceContainer(
			List<MdSchemaInstance> mdSchemaInstanceContainer) {
		this.mdSchemaInstanceContainer = mdSchemaInstanceContainer;
	}

	public void setAvailableSchemas(List<MdSchema> availableSchemas) {
		this.availableSchemas = availableSchemas;
	}

	public List<MdSchema> getAvailableSchemas() {
		return availableSchemas;
	}

	public boolean isSchemasPresent() {
		return schemasPresent;
	}

	public void setSchemasPresent(boolean schemasPresent) {
		this.schemasPresent = schemasPresent;
	}
	
	public MetadataFlowSchemas() {
		this.mdSchemaInstanceContainer = new ArrayList<MdSchemaInstance>();
		this.availableSchemas = new ArrayList<MdSchema>();
		// Assign Qudex Collections Spreadsheet by default
		this.typeSS = Constants.Q_COLLECTIONS_SS;
	}
	
	/**
	 * Metadata Schemas Initialization
	 */
	private void initialiseSchemas() {
		// ADD Dublin Core Schema
		Model auxModel = ModelFactory.createDefaultModel();
		MdSchema auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.DC_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = StaticJenaDublinCore.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("dc");
		auxSchema.setReadableName("Dublin Core Vocabulary");
		this.availableSchemas.add(auxSchema);

		// ADD Geo W3C lat/long
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.GEO_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = GeoSchema.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("geo");
		auxSchema.setReadableName("Basic Geo (WGS84 lat/long) Vocabulary");
		this.availableSchemas.add(auxSchema);

		// ADD RDF VOCABULARY
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.RDF_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = StaticJenaRDFS.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("rdf");
		auxSchema.setReadableName("RDF Vocabulary");
		this.availableSchemas.add(auxSchema);

		// ADD DCTERMS VOCABULARY
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.DCTERMS_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = StaticJenaDublinCoreTerms.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("dcterms");
		auxSchema.setReadableName("Dublin Core Terms");
		this.availableSchemas.add(auxSchema);

		// ADD Qudex VOCABULARY
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel = StaticQudex.getModel();
			auxSchema.setSchemaModel(auxModel);
			auxSchema.setPrefix("qudex");
			auxSchema.setReadableName("QuDex Vocabulary");
			this.availableSchemas.add(auxSchema);
		} catch (Exception e) {
			log.fatal(e.getMessage());
		}

		// ADD DDI2 VOCABULARY
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel = StaticDDI.getModel();
			auxSchema.setSchemaModel(auxModel);
			auxSchema.setPrefix("ddi2");
			auxSchema.setReadableName("DDI2 Vocabulary");
			this.availableSchemas.add(auxSchema);
		} catch (Exception e) {
			log.fatal(e.getMessage());
		}

		// ADD SKOS VOCABULARY
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.Skos_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = StaticSkos.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("skos");
		auxSchema.setReadableName("Skos Vocabulary");
		this.availableSchemas.add(auxSchema);

		// ADD Fedora Relationships Ontology
		auxModel = ModelFactory.createDefaultModel();
		auxSchema = new MdSchema();
		try {
			auxModel.read(RdfFunctions.RELS_Schema);
		} catch (Exception e) {
			log.info(e.getMessage());
			// Static load
			auxModel = FedoraRelsExtOntology.getModel();
		}
		auxSchema.setSchemaModel(auxModel);
		auxSchema.setPrefix("rel");
		auxSchema.setReadableName("Fedora relationships Ontology");
		this.availableSchemas.add(auxSchema);
	}
	
	public boolean schemasSelected() {
		for (MdSchemaInstance instance : this.mdSchemaInstanceContainer) {
			if (!instance.isRemoved())
				return true;
		}
		return false;
	}

	/**
	 * Removes the unused resources
	 */
	public void clean() {
		for (MdSchemaInstance instance : this.mdSchemaInstanceContainer) {
			if (instance.getSchemaModel() != null
					&& !instance.getSchemaModel().isClosed())
				instance.getSchemaModel().close();
		}
		this.mdSchemaInstanceContainer.clear();
		this.setSchemasPresent(false);
	}
	
	/**
	 * Initialisation and destroy methods
	 */
	public void destroy() {
		this.clean();
		for (MdSchema instance : this.availableSchemas) {
			if (instance.getSchemaModel() != null
					&& !instance.getSchemaModel().isClosed())
				instance.getSchemaModel().close();
		}

		this.availableSchemas.clear();
	}

	public void init() {
		this.initialiseSchemas();
	}

}
